package cz.jm.web.jmlib.client.dialog;

import java.util.ArrayList;
import java.util.List;

import com.google.gwt.core.shared.GWT;
import com.google.gwt.dom.client.Style.Unit;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.event.dom.client.KeyUpEvent;
import com.google.gwt.event.dom.client.KeyUpHandler;
import com.google.gwt.user.client.ui.Button;
import com.google.gwt.user.client.ui.Composite;
import com.google.gwt.user.client.ui.FocusPanel;
import com.google.gwt.user.client.ui.HasHorizontalAlignment;
import com.google.gwt.user.client.ui.HorizontalPanel;
import com.google.gwt.user.client.ui.Image;
import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.ScrollPanel;
import com.google.gwt.user.client.ui.VerticalPanel;
import com.google.gwt.user.client.ui.Widget;

import cz.jm.web.jmlib.client.dialog.items.DialogItem;
import cz.jm.web.jmlib.client.resources.Resources;

/** Zakladny widget pre dialogy. Poskytuje spolocnu funkcionalitu pre dialogy. Konktretne sa jedna 
 * o tlacitka (OK, Cancel, Next, Previous ...) a message box pre zobrazenie hlasok uzivatelovi. 
 * Primarny obsah sa do dialogu nastavuje pomocou {@linkplain DialogWidget#setDialogContent(DialogContent)}.]
 * <br><br>
 * Okrem standardnych buttonov (ktore su lokalizovane) je mozne vytvarat vlastne buttony pomocou 
 * {@linkplain #addButton(Button)}, {@linkplain #addButton(String)} a {@linkplain #addButton(String, ClickHandler)}.
 * <br><br>
 * Message box sa zobrazuje uplne dole v dialogu az pod buttonmi. Nie je to uplne standardna vlastnost 
 * dialogov ale vo webovom prostredi je to pomerne prakticky nastroj.
 * <br><br>
 * */
public class DialogWidget extends Composite implements DialogContent {
	
	private final DialogWidgetConstants constants = GWT.create(DialogWidgetConstants.class);

	/** Oddelovac sprav v oblasti sprav. */
	static private final String MESSAGE_DELIMITER = ";";
	
	/** Defaultna sirka buttonov. Je krajsie mat rovnaku sirku ako nechat velkost podla obsahu. */
	private static final String BUTTON_WIDTH = "100px";
	private static final int BUTTON_PADDING = 0;
	private static final int BUTTON_MARGIN = 2;	
	
	/** Panel pre cely dialog.*/
	private VerticalPanel mainLayout = new VerticalPanel();
	/** Panel pre primarny obsah dialogu, kt. sa injektuje z vonku. */
	private VerticalPanel dialogContentLayout =  new VerticalPanel();
	/** Layout pre buttony dialogu. */
	private HorizontalPanel buttonsLayout = new HorizontalPanel();
	/** Panel obsahujuci spravu. */
	private ScrollPanel messagePanel = new ScrollPanel();
	/** Sprava zobrazitelna pod buttonmi dialogu. */
	private Label messageLabel = new Label();
	/** Zoznam tlacidiel dialogu.*/
	private final List<Button> buttons = new ArrayList<Button>();	
	
	DialogContent dialogContent = new DialogContentIconWidget(new Image(Resources.INSTANCE.getNote()));
	
	/** Stlacenie enter sa berie ako click na tlacitko OK. 
	 * Funguje len ak je tlacitko Ok pritomne. Pozor rozlisenie buttonov prebieha 
	 * podla ich popisku. Popisky na buttonoch su lokalizovane. */
	private boolean enterAsOk = true;
	
	public DialogWidget() {
		final FocusPanel panel = new FocusPanel();
		panel.add(mainLayout);
		initWidget(panel);
		// Rekacia na enter ako na stlacenie buttonu OK
		panel.addKeyUpHandler(new KeyUpHandler() {
			
			@Override
			public void onKeyUp(KeyUpEvent event) {
				if (isEnterAsOk() && event.getNativeKeyCode() == 
						com.google.gwt.event.dom.client.KeyCodes.KEY_ENTER) {
					for (Button b : buttons) {
						if (b.getText().equals(constants.ok())) {
							b.click();
						}
					}
				}				
			}
		});
		
//		mainLayout.getElement().getStyle().setBackgroundColor("red");
//		dialogContentLayout.getElement().getStyle().setBackgroundColor("green");
//		buttonsLayout.getElement().getStyle().setBackgroundColor("blue");
//		messagePanel.getElement().getStyle().setBackgroundColor("orange");
//		messageLabel.getElement().getStyle().setBackgroundColor("blue");
		
		mainLayout.add(dialogContentLayout);
		mainLayout.add(buttonsLayout);		
		messagePanel.add(messageLabel);
		mainLayout.add(messagePanel);		

		mainLayout.setCellHorizontalAlignment(buttonsLayout, HasHorizontalAlignment.ALIGN_RIGHT);
		
		setDialogContent(dialogContent);
	}
	
	/** Nastavenie velkosti oblasti sprav pod buttonmi. Pokial sprava zaberie viac priestoru 
	 * ako je nastavene tak sa zobrazia scrollbary. */
	public void setMessageSize(String width, String height) {
		mainLayout.setCellHeight(messagePanel, height);
		messagePanel.setSize("100%", height);
		messageLabel.setSize(width, height);
	}
	

	
	/** Pridanie spravy do oblasti sprav so zachovanim uz zobrazenych sprav.
	 * Nove spravy sa pridavaju na zaciatok a stare sa postupne posuvaju dalej. 
	 * Spravy su oddelene pomocou {@linkplain #MESSAGE_DELIMITER}.
	 * */
	public void addMessage(String message) {
		String oldMessage = messageLabel.getText();
		if (oldMessage.isEmpty()) {
			setMessage(message);
		}
		else {
			setMessage(message + MESSAGE_DELIMITER + oldMessage);
		}
	}
	
	/** Zmaze vsetky spravy v oblasti sprav.*/
	public void clearMessages() {
		setMessage("");
	}
	
	/** Nastavenie novej spravy do oblasti sprav. Vsetky predosle zobrazene spravy su 
	 * z oblasti sprav zmazane. */
	public void setMessage(String message) {
		messageLabel.setText(message);
	}
	
	/** Nastavenie farby textu v oblasti sprav.
	 * */
	public void setMessageColor(String color) {
		messageLabel.getElement().getStyle().setColor(color);
	}
	
	
	/** Pridanie tlacidla do oblasti tlacitiel. 
	 * Najobecnejsie forma pridania tlacidla, ktora umoznuje vlozit hotove tlacidlo.
	 *  
	 * @see DialogWidget#getButton(int)
	 * @return handle ktorym je button dostupny */
	public int addButton(Button button) {
		buttons.add(button);
		
		buttonsLayout.add(button);	
		buttonsLayout.setCellHorizontalAlignment(button, HasHorizontalAlignment.ALIGN_RIGHT);
		
		return buttons.size() - 1;
	}
	
	/** Vytvori tlacidlo s popiskom label. 
	 * Nove tlacitko je dostupne pomocou {@linkplain #getButton(int)}.
	 * 
	 * @return handle pre vytvoreny button */
	public int addButton(String label) {
		return addButton(label, null);
	}

	/** Vytvori tlacidlo s popiskom label a handlarom pre kliknutie. 
	 * Nove tlacitko je dostupne pomocou {@linkplain #getButton(int)}.
	 * 
	 * @return handle pre vytvoreny button */
	public int addButton(String label, ClickHandler clickHandler) {
		Button b = new Button(label);		
		if (clickHandler != null) {
			b.addClickHandler(clickHandler);
		}
		initButton(b);
		return addButton(b);
	}
	
	/** Vrati button ktory bol vytvoreny ci pridany z handlom, ktory je predany ako parameter. */
	public Button getButton(int buttonHandle) {
		return buttons.get(buttonHandle);
	}
	
	/** Varti prvy button (teda button s handlom 0). Pokial neexistuje ziaden button 
	 * tak vrati 0. */
	public Button getButton() {
		if (buttons.isEmpty()) return null;
		return buttons.get(0);
	}

	/** Nastavenie obsahu (tela) dialogu. Tento objekt tvori len opornu kostru dialogu ale 
	 * hlavne telo musi byt injektovane z vonku. */
	public void setDialogContent(DialogContent dialogContent) {
		setContentWidget(dialogContent.asWidget());
		this.dialogContent = dialogContent;
	}
	
	/**
	 * Zastarale nastavenie widgetu ako tela dialogu. Metoda je ponechana kvoli spatnej kompatibilite 
	 * ale pouzivat je nie je vhodne, pretoze nefunguje validacia ci vkladanie poloziek dialogu priamo 
	 * do dialogu.
	 * @deprecated Pouzivat {@linkplain #setDialogContent(DialogContent)}
	 * */
	@Deprecated 
	public void setDialogContentWidget(Widget w) {
		if (w instanceof DialogContent) {
			setDialogContent((DialogContent)w);
		}
		else {
			dialogContent = DialogContent.EMPTY_CONTENT;
			setContentWidget(w);
		}		
	}
	
	/** Fyzicka zmena vizualizacie obsahu dialogu. */
	private void setContentWidget(Widget w) {
		dialogContentLayout.clear();
		dialogContentLayout.add(w);
	}


	public int addOkButton() {
		return addButton(constants.ok());
	}
	
	public int addCancelButton() {
		return addButton(constants.cancel());		
	}
	
	public int addNextButton() {
		return addButton(constants.next());		
	}
	
	public int addPreviousButton() {
		return addButton(constants.previous());		
	}
	
	private void initButton(Button button) {
		button.setWidth(BUTTON_WIDTH);
		button.getElement().getStyle().setPadding(BUTTON_PADDING, Unit.PX);
		button.getElement().getStyle().setMargin(BUTTON_MARGIN, Unit.PX);
	}

	/** Je nastavene stlacenie enteru ako kliknutie na button OK? */
	public boolean isEnterAsOk() {
    	return enterAsOk;
    }

	/** Stlacenie enter sa berie ako click na tlacitko OK. 
	 * Funguje len ak je tlacitko Ok pritomne. Pozor rozlisenie buttonov prebieha 
	 * podla ich popisku. Popisky na buttonoch su lokalizovane. */
	public void setEnterAsOk(boolean enterAsOk) {
    	this.enterAsOk = enterAsOk;
    }

	@Override
    public boolean isValid() {
	    return dialogContent.isValid();
    }

	@Override
    public void addItem(DialogItem item) {
		dialogContent.addItem(item);	    
    }

	@Override
    public void setIcon(Image icon) {
		dialogContent.setIcon(icon);	    
    }
}
